package com.runner.player;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.ParticleEffect;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.runner.entity.CollisionEntity;
import com.runner.observer.Event;
import com.runner.observer.Subject;
import com.runner.player.input.NullInputComponent;
import com.runner.player.input.PlayerInputComponent;
import com.runner.player.state.NullState;
import com.runner.player.state.PlayerState;
import com.runner.track.Block;
import com.runner.utility.Constants;
import com.runner.utility.Locator;

public abstract class Player extends CollisionEntity {

	public static final float JUMP_GRAVITY_PEAKED_SCALE = 1f;
	public static final float SCALE=0.5F;

	protected PlayerInputComponent input;
	protected PlayerState state;
	protected Subject subject;
	protected boolean hasShield;
	protected boolean win;
	protected boolean death;
	protected Vector3 parPos;
	protected ParticleEffect particles;
	protected boolean running;

	protected Sprite jumpSprite;
	protected Sprite slideSprite;
	protected Sprite runSprite;

	protected Animation animation;
	protected TextureRegion[] frames;
	protected TextureRegion currentFrame;
	protected float stateTime;

	public Player(Vector2 pos, TYPE type, String path, float scale, Subject subject) {
		super(2, 1, pos, type, path, false, false, true, scale);
		input = new NullInputComponent(this);
		state = new NullState(this);
		this.subject = subject;
		win = false;
		running = true;

		// animation
		int cols = 2;
		int rows = 1;
		Texture sheet = this.getSprite().getTexture();
		TextureRegion[][] tmp = TextureRegion.split(sheet, sheet.getWidth() / cols, sheet.getHeight() / rows);
		frames = new TextureRegion[cols * rows];
		int index = 0;
		for (int i = 0; i < rows; i++) {
			for (int j = 0; j < cols; j++) {
				frames[index++] = tmp[i][j];
			}
		}
		float animTime = 0.25f / (float) (cols * rows);
		animation = new Animation(animTime, frames);
		stateTime = 0f;
		currentFrame = animation.getKeyFrame(stateTime, true);
	}

	@Override
	public void render(SpriteBatch sb) {
		if (running) {
			super.render(sb, currentFrame);
		} else {
			super.render(sb);
		}
	}

	public void update(float delta) {
		super.update(delta);

		// animation
		if (running) {
			stateTime += delta;
			currentFrame = animation.getKeyFrame(stateTime, true);
		}

		if (death) {
			parPos.add(Block.SPEED * Constants.SCALE * Locator.getXGameSpeed() * -delta, 0, 0);
			Vector3 tmp = new Vector3(parPos);
			Locator.getCamera().project(tmp);
			particles.getEmitters().get(0).setPosition(tmp.x, tmp.y);
		} else {
			// dont change states if player has died
			state.update(delta);
		}
	}

	public void setRunning(boolean b) {
		running = b;
	}

	public void addShield() {
		hasShield = true;
	}

	public void changeState(PlayerState state) {
		this.state = state;
	}

	public void jump() {
		if (!death) {
			state.newAction(INPUT.JUMP);
			this.setSprite(getJumpSprite());
		}
	}

	public void roll() {
		if (!death) {
			state.newAction(INPUT.ROLL);
			this.setSprite(getSlideSprite());
		}
	}
	
	public void dive() {
		if (!death) {
			state.newAction(INPUT.DIVE);
			this.setSprite(getJumpSprite());
		}
	}
	
	public void touchUp() {
		if (!death) {
			state.newAction(INPUT.TOUCH_UP);
		}
	}

	public PlayerInputComponent getInput() {
		return input;
	}

	public void setInput(PlayerInputComponent input) {
		this.input = input;
	}

	public Subject getSubject() {
		return subject;
	}

	public void setSubject(Subject subject) {
		this.subject = subject;
	}

	public Vector2 getJumpVelocity() {
		return new Vector2();
	}

	@Override
	public void collide(CollisionEntity otherBody) {
		if (otherBody.getPhysics().getType().equals(TYPE.COIN)) {
			subject.notify(Event.EVENT_COIN);
		} else if (otherBody.getPhysics().getType().equals(TYPE.BLOCK)) {
			if (!hasShield) {
				if (!death) {
					death = true;
					running=false;
					touchUp();
					setSprite(getJumpSprite());
					subject.notify(Event.EVENT_DEATH);
					// particles from destroyed block
					particles = new ParticleEffect();
					particles.load(Gdx.files.internal("data/art/block_explode"), Gdx.files.internal("data/art/"));
					Locator.getEffects().add(particles);
					parPos = new Vector3(this.getPhysics().getBody().getPosition().x, this.getPhysics().getBody().getPosition().y, 0);
					Vector3 tmp = new Vector3(parPos);
					Locator.getCamera().project(tmp);
					particles.setPosition(tmp.x, tmp.y);
					particles.start();
				}
			} else {
				subject.notify(Event.HIT_WITH_SHIELD);
				//destroy block when shield hits it
				otherBody.setExists(false);
			}
		} else if (otherBody.getPhysics().getType().equals(TYPE.GOAL)) {
			if (!win) {
				win = true;
				subject.notify(Event.EVENT_WIN);
			}
		}
	}

	public abstract PLAYER_TYPE getPlayerType();

	public float getJumpGravityScale() {
		return 0;
	}

	public float getJumpTimer() {
		return 0;
	}

	public boolean hasShield() {
		return hasShield;
	}

	public void removeShield() {
		hasShield = false;
	}

	public Sprite getJumpSprite() {
		return jumpSprite;
	}

	public Sprite getSlideSprite() {
		return slideSprite;
	}

	public Sprite getRunSprite() {
		return runSprite;
	}

}
